/*
 *  Copyright 2008-2011 NVIDIA Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include <thrust/detail/config.h>
#include <thrust/omp/memory.h>
#include <cstdlib> // for malloc & free
#include <new>     // for std::bad_alloc
#include <limits>

namespace thrust
{
namespace omp
{

template<typename T>
  template<typename OtherT>
    reference<T> &
      reference<T>
        ::operator=(const reference<OtherT> &other)
{
  super_t::operator=(other);
  return *this;
} // end reference::operator=()

template<typename T>
  reference<T> &
    reference<T>
      ::operator=(const value_type &x)
{
  super_t::operator=(x);
  return *this;
} // end reference::operator=()

template<typename T>
__host__ __device__
void swap(reference<T> &a, reference<T> &b)
{
  a.swap(b);
} // end swap()

pointer<void> malloc(std::size_t n)
{
  void *result = std::malloc(n);

  // XXX not sure if malloc should throw
  //     probably only new should
  if(!result)
  {
    throw std::bad_alloc();
  } // end if

  return pointer<void>(result);
} // end malloc()

void free(pointer<void> ptr)
{
  std::free(ptr.get());
} // end free()


// allocator follows

template<typename T>
  allocator<T>
    ::allocator()
{}

template<typename T>
  allocator<T>
    ::allocator(const allocator<T> &)
{}

template<typename T>
  template<typename U>
    allocator<T>
      ::allocator(const allocator<U> &)
{}

template<typename T>
  allocator<T>
    ::~allocator()
{}

template<typename T>
  typename allocator<T>::pointer
    allocator<T>
      ::address(reference x) const
{
  return &x;
} // end allocator::address()

template<typename T>
  typename allocator<T>::const_pointer
    allocator<T>
      ::address(const_reference x) const
{
  return &x;
} // end allocator::address()

template<typename T>
  typename allocator<T>::pointer
    allocator<T>
      ::allocate(size_type cnt, allocator<void>::const_pointer hint)
{
  if(cnt > this->max_size())
  {
    throw std::bad_alloc();
  } // end if

  // XXX this is a bit ugly
  return pointer(static_cast<T*>(thrust::omp::malloc(cnt * sizeof(T)).get()));
} // end allocator::deallocatet()

template<typename T>
  void allocator<T>
    ::deallocate(pointer p, size_type n)
{
  thrust::omp::free(p);
} // end allocator::deallocate()

template<typename T>
  typename allocator<T>::size_type
    allocator<T>
      ::max_size() const
{
  return (std::numeric_limits<size_type>::max)() / sizeof(T);
} // end allocator<T>::max_size()

template<typename T1, typename T2>
  bool operator==(const allocator<T1> &, const allocator<T2> &)
{
  return true;
} // end operator==()

template<typename T1, typename T2>
  bool operator!=(const allocator<T1> &, const allocator<T2> &)
{
  return false;
} // end operator==()

} // end namespace omp

namespace detail
{

// XXX iterator_facade tries to instantiate the Reference
//     type when computing the answer to is_convertible<Reference,Value>
//     we can't do that at that point because reference
//     is not complete
//     WAR the problem by specializing is_convertible
template<typename T>
  struct is_convertible<thrust::omp::reference<T>, T>
    : thrust::detail::true_type
{};

namespace backend
{

// specialize dereference_result and overload dereference
template<typename> struct dereference_result;

template<typename T>
  struct dereference_result<thrust::omp::pointer<T> >
{
  typedef T& type;
}; // end dereferenc_result

template<typename T>
  struct dereference_result<thrust::omp::pointer<const T> >
{
  typedef const T& type;
}; // end dereferenc_result

template<typename T>
  typename dereference_result< thrust::omp::pointer<T> >::type
    dereference(thrust::omp::pointer<T> ptr)
{
  return *ptr.get();
} // end dereference()

template<typename T, typename IndexType>
  typename dereference_result< thrust::omp::pointer<T> >::type
    dereference(thrust::omp::pointer<T> ptr, IndexType n)
{
  return ptr.get()[n];
} // end dereference()

} // end backend

} // end namespace detail
} // end namespace thrust

